namespace Sop.Data.Id;

public class IdSnowflakeUtil
  {
    private static long _machineId;
    private static long _datacenterId = 0;
    private static long _sequence = 0;
    private static readonly long twepoch = 687888001020;
    private static long machineIdBits = 5;
    private static readonly long datacenterIdBits = 5;
    private static readonly long maxMachineId = -1L ^ -1L << (int) IdSnowflakeUtil.machineIdBits;
    private static readonly long maxDatacenterId = -1L ^ -1L << (int) IdSnowflakeUtil.datacenterIdBits;
    private static readonly long _sequenceBits = 12;
    private static readonly long _machineIdShift = IdSnowflakeUtil._sequenceBits;
    private static readonly long _datacenterIdShift = IdSnowflakeUtil._sequenceBits + IdSnowflakeUtil.machineIdBits;
    private static readonly long _timestampLeftShift = IdSnowflakeUtil._sequenceBits + IdSnowflakeUtil.machineIdBits + IdSnowflakeUtil.datacenterIdBits;
    private static readonly long _sequenceMask = -1L ^ -1L << (int) IdSnowflakeUtil._sequenceBits;
    private static long _lastTimestamp = -1;
    private static readonly object _syncRoot = new object();
    private static IdSnowflakeUtil _snowflake;

    private void Snowflakes(long machineId, long datacenterId)
    {
      if (machineId >= 0L)
        IdSnowflakeUtil._machineId = machineId <= IdSnowflakeUtil.maxMachineId ? machineId : throw new Exception("机器码ID非法");
      if (datacenterId < 0L)
        return;
      IdSnowflakeUtil._datacenterId = datacenterId <= IdSnowflakeUtil.maxDatacenterId ? datacenterId : throw new Exception("数据中心ID非法");
    }

    private static long GetTimestamp() => (long) (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;

    private static long GetNextTimestamp(long lastTimestamp)
    {
      long timestamp = IdSnowflakeUtil.GetTimestamp();
      int num = 0;
      for (; timestamp <= lastTimestamp; timestamp = IdSnowflakeUtil.GetTimestamp())
      {
        ++num;
        if (num > 10)
          throw new Exception("机器的时间可能不对");
        Thread.Sleep(1);
      }
      return timestamp;
    }

    public static IdSnowflakeUtil Instance()
    {
      if (IdSnowflakeUtil._snowflake == null)
        IdSnowflakeUtil._snowflake = new IdSnowflakeUtil();
      return IdSnowflakeUtil._snowflake;
    }

    public IdSnowflakeUtil() => this.Snowflakes(0L, -1L);

    public IdSnowflakeUtil(long machineId) => this.Snowflakes(machineId, -1L);

    public IdSnowflakeUtil(long machineId, long datacenterId) => this.Snowflakes(machineId, datacenterId);

    public long GetId()
    {
      lock (IdSnowflakeUtil._syncRoot)
      {
        long num = IdSnowflakeUtil.GetTimestamp();
        if (IdSnowflakeUtil._lastTimestamp == num)
        {
          IdSnowflakeUtil._sequence = IdSnowflakeUtil._sequence + 1L & IdSnowflakeUtil._sequenceMask;
          if (IdSnowflakeUtil._sequence == 0L)
            num = IdSnowflakeUtil.GetNextTimestamp(IdSnowflakeUtil._lastTimestamp);
        }
        else
          IdSnowflakeUtil._sequence = 0L;
        IdSnowflakeUtil._lastTimestamp = num >= IdSnowflakeUtil._lastTimestamp ? num : throw new Exception("时间戳比上一次生成ID时时间戳还小，故异常");
        return num - IdSnowflakeUtil.twepoch << (int) IdSnowflakeUtil._timestampLeftShift | IdSnowflakeUtil._datacenterId << (int) IdSnowflakeUtil._datacenterIdShift | IdSnowflakeUtil._machineId << (int) IdSnowflakeUtil._machineIdShift | IdSnowflakeUtil._sequence;
      }
    }
  }